package com.yb0os1;

import com.yb0os1.anno.Yb0os1Field;
import com.yb0os1.anno.Yb0os1Param;
import com.yb0os1.anno.Yb0os1TableName;
import jdk.swing.interop.SwingInterOpUtils;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.sql.Array;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.StringJoiner;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

public class MySqlsessionFactory {
    private static final String JDBCURL = "jdbc:mysql://localhost:3306/testdb";
    private static final String USERNAME = "root";
    private static final String PASSWORD = "root";

    public <T> T getMapper(Class<T> mapperClass) {
        return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{mapperClass}, new SqlInvocationHandler());
    }

    private class SqlInvocationHandler implements InvocationHandler {
        /**
         * @param proxy  代理对象
         * @param method 被代理方法
         * @param args   被代理的方法参数
         * @return 被代理方法的返回值
         */
        public Object invoke(Object proxy, Method method, Object[] args) {
            //Object对象的方法不走代理
            if (Object.class.equals(method.getDeclaringClass())) {
                throw new RuntimeException("Object对象的方法不走代理");
            }
            if (method.getName().startsWith("selectBatch")) {
                //selectBatch开头的走下面的代理
                return invokeSelect(proxy, method, args,true);
            }else if (method.getName().startsWith("select")) {
                //select开头的走下面的代理
                return invokeSelect(proxy, method, args,false);
            }else if (method.getName().startsWith("insert")) {
                //insert开头的走下面的代理
                return invokeInsert(proxy, method, args);
            }
            return null;
        }
        //插入操作
        private Object invokeInsert(Object proxy, Method method, Object[] args) {
            //insert into table () values ();
            StringBuilder sqlBuilder = new StringBuilder("insert into ");
            Parameter[] parameters = method.getParameters();
            Class<?> target = parameters[0].getType();
            String tableName = getTableName(target.getAnnotation(Yb0os1TableName.class));
            sqlBuilder.append(tableName);
            Field[] fields = target.getDeclaredFields();
            String colNames = Arrays.stream(fields).map(field -> field.getAnnotation(Yb0os1Field.class).name()).collect(Collectors.joining(","));
            sqlBuilder.append(" (").append(colNames).append(") ");
            String nums = Arrays.stream(fields).map(field -> "?").collect(Collectors.joining(","));
            sqlBuilder.append("values (").append(nums).append(")");

            String sql = sqlBuilder.toString();
            System.out.println("执行的sql语句="+sql);
            System.out.println("传入的参数="+ Arrays.toString(args));
            try (Connection conn = DriverManager.getConnection(JDBCURL, USERNAME, PASSWORD);
                 PreparedStatement ps = conn.prepareStatement(sql)) {
                for (int i = 0; i < args.length; i++) {
                    Field[] fieldsTarget = args[i].getClass().getDeclaredFields();
                    int count = 1;
                    for (Field field : fieldsTarget){
                        if (field.getType().equals(Integer.class)){
                            ps.setInt(count++,(Integer)field.get(args[i]));
                        }else if (field.getType().equals(String.class)){
                            ps.setString(count++,(String)field.get(args[i]));
                        }else {
                            throw new RuntimeException("目前只支持Integer和String类型");
                        }
                    }
                }
                int execute = ps.executeUpdate();
                return execute!=0;
            }catch (Exception e){
                throw new RuntimeException(e);
            }
        }


        private Object invokeSelect(Object proxy, Method method, Object[] args,boolean isBatch) {
            //组成sql语句的方法  select xxx from xxx where xxx = ?
            String sql = isBatch?composeSelectBatchSql(method,args) : composeSelectSql(method);
            System.out.println("执行的sql语句="+sql);
            System.out.println("传入的参数="+ Arrays.toString(args));
            try (Connection conn = DriverManager.getConnection(JDBCURL, USERNAME, PASSWORD);
                 PreparedStatement ps = conn.prepareStatement(sql)) {
                //设置参数的值
                int count = 1;
                for (int i = 0; i < args.length; i++) {
                    Object arg = args[i];
                    if (arg instanceof Integer) {
                        ps.setInt(i + 1, (Integer) arg);
                    } else if (arg instanceof String) {
                        ps.setString(i + 1, (String) arg);
                    }else if(arg instanceof Collection<?> collection){
                        Object[] array = collection.toArray();
                        if (array.length > 0 && array[0] instanceof Integer) {
                            for (Object o : array) {
                                ps.setInt(count++, (Integer) o);
                            }
                        } else if (array.length > 0 && array[0] instanceof String) {
                            for (Object o : array) {
                                ps.setString(count++, (String) o);
                            }
                        } else {
                            throw new RuntimeException("目前只支持Integer和String类型");
                        }
                    }
                    else {
                        throw new RuntimeException("目前只支持Integer和String类型");
                    }
                }
                //得到结果集
                ResultSet rs = ps.executeQuery();
               if (Collection.class.isAssignableFrom(method.getReturnType())||isBatch){
                   Collection<Object> collection = new ArrayList<>();
                   while (rs.next()){
                       collection.add(parseResult(rs, getClassFromMethod(method)));
                   }
                   return collection;
               }else{
                   if (rs.next()) {
                       //创建对象 需要通过反射
                       return parseResult(rs, method.getReturnType());
                   }
               }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            return null;
        }

        private Object parseResult(ResultSet rs, Class<?> returnType) throws Exception {
            Constructor<?> constructor = returnType.getConstructor();
            Object res = constructor.newInstance();
            Field[] fields = returnType.getDeclaredFields();
            for (Field field : fields) {
                String name = field.getName();
                Object column = null;
                if (field.getType().equals(String.class)) {
                    column = rs.getString(name);
                } else if (field.getType().equals(Integer.class)) {
                    column = rs.getInt(name);
                } else {
                    throw new RuntimeException("目前只支持Integer和String类型");
                }
                field.setAccessible(true);
                field.set(res, column);
            }
            return res;
        }
        private String composeSelectBatchSql(Method method, Object[] args) {
            //select xxx from xxx where xxx in (?,?,?)
            StringBuilder sql = new StringBuilder("select ");
            Class<?> aClass = getClassFromMethod(method);
            sql.append(getSelectCols(aClass));
            sql.append(" from ");
            sql.append(getTableName(aClass.getAnnotation(Yb0os1TableName.class)));
            sql.append(" where ");
            sql.append(getSelectBatchConditions(method.getParameters(),args));
            return sql.toString();
        }

        private String getSelectBatchConditions(Parameter[] parameters, Object[] args) {
            AtomicInteger i = new AtomicInteger();
            List<String> collect = Arrays.stream(args).map(arg -> {
                StringJoiner sb = new StringJoiner(",");
                int size = ((Collection<?>) arg).size();
                for (int j = 0; j < size; j++) {
                    sb.add("?");
                }
                return sb.toString();
            }).toList();
            return Arrays.stream(parameters)
                    .map(parameter -> {
                                String s = collect.get(i.getAndIncrement());
                                return parameter.getAnnotation(Yb0os1Param.class).value() + " in("+s+")";
                    }
                    )
                    .collect(Collectors.joining(" and "));
        }

        private Class<?> getClassFromMethod(Method method) {
            Class<?> aClass = null;
            // 获取方法的返回类型
            Type returnType = method.getGenericReturnType();
            if (returnType instanceof ParameterizedType parameterizedType) {
                // 获取泛型参数类型
                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                aClass = (Class<?>) actualTypeArguments[0];
            } else {
                throw new RuntimeException("The method's return type is not a parameterized type.");
            }
            return aClass;
        }

        private String composeSelectSql(Method method) {
            //select xxx,xxx from xxx where xxx=?
            Class<?> returnType = method.getReturnType();
            if (Collection.class.isAssignableFrom(returnType)){
                returnType = getClassFromMethod(method);
            }
            StringBuilder sql = new StringBuilder("select ");
            String selectCols = getSelectCols(returnType);
            String tableName = getTableName(returnType.getAnnotation(Yb0os1TableName.class));
            String conditions = getzSelectConditions(method.getParameters());
            return sql.append(selectCols)
                    .append(" from ")
                    .append(tableName)
                    .append(" where ")
                    .append(conditions)
                    .toString();
        }

        private String getzSelectConditions(Parameter[] parameters) {
            return Arrays.stream(parameters).map(parameter -> parameter.getAnnotation(Yb0os1Param.class).value() + "=?")
                    .collect(Collectors.joining(" and "));
        }

        private String getTableName(Yb0os1TableName annotation) {
            if (annotation == null) {
                throw new RuntimeException("请为该类添加@Yb0os1TableName注解");
            }
            return annotation.name();
        }

        private String getSelectCols(Class<?> returnType) {
            //return :: User
            //获取所有的字段；
            return Arrays.stream(returnType.getDeclaredFields()).map(Field::getName)
                    .collect(Collectors.joining(","));
        }

    }
}
